#![feature(proc_macro_hygiene, decl_macro)]

use std::io::Cursor;
use std::fs::{File, remove_file};
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use ho_string::ho_string::HoString;
use ho_file::HoFile;
use ho_bytes::ho_bytes::HoBytes;
use rocket::{custom, routes, get, post, Data, State};
use rocket::config::{Config, Environment};
use rocket::http::{ContentType, Cookie, Cookies};
use rocket::response::Response;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::json::Json;
use rocket::request::{Form, FromForm};
use mysql::from_row;
use mysql::prelude::Queryable;
use mysql::{Pool, PooledConn, Row, params};
use bcrypt::{hash, verify, DEFAULT_COST, BcryptResult};
use math::round::ceil;
use serde_json::Value;
use serde_json::value::to_value;
use serde::Serialize;
use tar::Builder;
use image::{GenericImageView, DynamicImage};

#[derive(Clone)]
#[allow(dead_code)]
struct UserInfo {
    id: usize,
    account: String,
    email: String,
    tel: String,
    alipay_account: String,
    alipay_name: String,
    qq_number: String,
    push_id: usize
}

#[derive(Clone)]
struct MySqlDriver {
    driver: String,
    base_url: String
}

include!("cookies/cookies.rs");
include!("user/rules.rs");

fn get_menu(mod_id: String, mod_menu_open: String, sub_mod_id: String, 
    user_info: UserInfo, state: &State<MySqlDriver>) -> String {
    let id: usize = user_info.id;
    let sql: String = format!("SELECT COUNT(`m`.`id`) AS `count` \
        FROM `message` AS `m` \
        INNER JOIN `copywrite_order` AS `copy_o` ON `copy_o`.`id` = `m`.`copywrite_order_id` \
        INNER JOIN `tag_group` AS `tg` ON `tg`.`id` = `m`.`receive_group_id` \
        WHERE (\
                `m`.`receive_user_id` = {} OR \
                `tg`.`admin_id_examine` = {} OR \
                `tg`.`admin_id_publish` = {}\
                ) AND \
                `m`.`is_read` = 0", id, id, id);
    let pool_result: Result<Pool, mysql::Error> = Pool::new(state.driver.as_str());
    let pool: Pool;
    if let Ok(p) = pool_result {
        pool = p;
    } else {
        return "fail:无法建立数据库连接".to_string();
    }
    let conn_res: Result<PooledConn, mysql::Error> = pool.get_conn();
    let mut conn: PooledConn;
    if let Ok(res) = conn_res {
        conn = res;
    } else {
        return "fail:无法获取数据库连接".to_string();
    }
    let result_rows: Result<Option<Row>, mysql::Error> = conn.query_first(sql);
    let mut count: usize = 0;
    let mut new_display: String = "inline".to_string();
    if let Ok(Some(r)) = result_rows {
        let count_op: Option<String> = r.get("count");
        if let Some(c) = count_op {
            if let Ok(i) = c.parse::<usize>() {
                count = i;
            }
            if count == 0 {
                new_display = "none".to_string();
            }
        }
    }

    let mut menu_src_vec: Vec<&[u8]> = Vec::new();
    let mut menu_replace_vec: Vec<&[u8]> = Vec::new();

    menu_src_vec.push("{base_url}".as_bytes());
    menu_src_vec.push("{new_display}".as_bytes());
    menu_src_vec.push("{mod_id}".as_bytes());
    menu_src_vec.push("{mod_menu_open}".as_bytes());
    menu_src_vec.push("{sub_mod_id}".as_bytes());
    menu_replace_vec.push(state.base_url.as_bytes());
    menu_replace_vec.push(new_display.as_bytes());
    menu_replace_vec.push(mod_id.as_bytes());
    menu_replace_vec.push(mod_menu_open.as_bytes());
    let sub_mod_id_str: String = format!("$('#{}').addClass('active');", sub_mod_id.clone());
    if sub_mod_id.len() > 0 {
        menu_replace_vec.push(sub_mod_id_str.as_bytes());
    } else {
        menu_replace_vec.push("".as_bytes());
    }

    let mut menu_string: HoString = HoFile::read_file_bytes("web/menu.html");
    menu_string = menu_string.replace_bytes_vec(menu_src_vec, menu_replace_vec);
    return menu_string.to_str().to_string();
}

fn get_header(mod_id: String, mod_menu_open: String, sub_mod_id: String, 
    page_title: String, title: String, cookies: &Cookies, state: &State<MySqlDriver>) -> String {
    let user_info: UserInfo = get_cookies(&cookies);

    let menu_page: String = get_menu(mod_id, mod_menu_open, sub_mod_id, user_info.clone(), state);
    let file_content: HoString = HoFile::read_file_all(
        format!("./ws/ws_session/{}.txt", user_info.id.to_string().as_str()).as_str(), "");
    let ws_sub_index: String;
    if file_content.length() == 0 {
        ws_sub_index = "0".to_string();
    } else {
        let ws_list: Vec<HoString> = file_content.split_str("|");
        ws_sub_index = ws_list.len().to_string();
    }

    let mut header_src_vec: Vec<&[u8]> = Vec::new();
    let mut header_replace_vec: Vec<&[u8]> = Vec::new();

    header_src_vec.push("{page_title}".as_bytes());
    header_src_vec.push("{base_url}".as_bytes());
    header_src_vec.push("{ws_url}".as_bytes());
    header_src_vec.push("{uid}".as_bytes());
    header_src_vec.push("{ws_sub_index}".as_bytes());
    header_src_vec.push("{uaccount}".as_bytes());
    header_src_vec.push("{title}".as_bytes());
    header_src_vec.push("{menu}".as_bytes());
    header_replace_vec.push(page_title.as_bytes());
    header_replace_vec.push(state.base_url.as_bytes());
    header_replace_vec.push("ws://127.0.0.1:2048/".as_bytes());
    let user_id: String = user_info.id.to_string();
    header_replace_vec.push(user_id.as_bytes());
    header_replace_vec.push(ws_sub_index.as_bytes());
    header_replace_vec.push(user_info.account.as_bytes());
    header_replace_vec.push(title.as_bytes());
    header_replace_vec.push(menu_page.as_bytes());

    let mut header_string: HoString = HoFile::read_file_bytes("web/header.html");
    header_string = header_string.replace_bytes_vec(header_src_vec, header_replace_vec);
    return header_string.to_str().to_string();
}

fn get_footer() -> String {
    return HoFile::read_file_bytes("web/footer.html").to_str().to_string();
}

#[post("/library/revoke_15day")]
fn revoke_15day(_cookies: Cookies, state: State<MySqlDriver>) -> String {
    let now: SystemTime = SystemTime::now();
    let since_epoch: Duration = now.duration_since(UNIX_EPOCH).unwrap();
    let timestamp: u64 = since_epoch.as_secs() - 15 * 24 * 60 * 60;
    let pool: Pool = Pool::new(state.driver.as_str()).unwrap();
    let mut conn: PooledConn = pool.get_conn().unwrap();

    let mut sql: String = format!("UPDATE `copywrite_order` SET `status` = 0, `get_user_id` = 0, `update_datetime` = 0 WHERE `update_datetime` <> 0 AND `update_datetime` < {}", timestamp);
    let rows1: Vec<Row> = conn.query(sql.clone()).unwrap();
    let mut count: usize = rows1.len();

    sql = format!("UPDATE `clip_order` SET `status` = 0, `get_user_id` = 0, `update_datetime` = 0 WHERE `update_datetime` <> 0 AND `update_datetime` < {}", timestamp);
    let rows2: Vec<Row> = conn.query(sql.clone()).unwrap();
    count += rows2.len();
    return count.to_string();
}

include!("user/login.rs");
include!("user/register.rs");
include!("copywrite/list.rs");
include!("copywrite/edit.rs");
include!("topic/list.rs");
include!("topic/edit.rs");
include!("task/list.rs");
include!("task/edit.rs");
include!("spread/list.rs");
include!("examine/list.rs");
include!("examine/view.rs");
include!("group/list.rs");
include!("group/edit.rs");
include!("clip/list.rs");
include!("clip/view.rs");
include!("publish/list.rs");
include!("msg/list.rs");
include!("msg/view.rs");
include!("user/list.rs");
include!("user/admin_list.rs");
include!("user/person_info.rs");
include!("admin/global.rs");
include!("ws_lib/clip_list.rs");
include!("ws_lib/copywrite_list.rs");
include!("ws_lib/examine.rs");
include!("ws_lib/menu.rs");
include!("ws_lib/task_list.rs");

fn main() {
    let config: Config = Config::build(Environment::Development)
        .address("0.0.0.0")
        .port(8080)
        .workers(1000)
        .unwrap();
    let routes = routes![
        login,
        user_login,
        register,
        reg,
        copywrite_list_4,
        copywrite_list_2,
        copywrite_list_empty,
        get_copywrite_topic,
        copywrite_add,
        add_copywrite_order,
        copywrite_edit,
        update_copywrite,
        get_copywrite_order,
        copywrite_file_upload,
        topic_list,
        remove_topic,
        add_topic,
        bt_file_upload,
        upload_topic,
        edit_topic,
        update_topic,
        task_list,
        spread_list,
        view_copywrite_task,
        upload_task_order,
        wait_for_examine,
        examine_copywrite_order,
        upload_dub_file,
        accept_copywrite_order,
        refuse_copywrite_order,
        group_list,
        add_group,
        edit_group,
        upload_group,
        remove_group,
        update_group,
        clip_list,
        get_clip_order,
        view_clip_order,
        upload_order_msg,
        view_clip_task,
        order_history_list,
        clip_image_upload,
        clip_video_upload,
        upload_clip_order,
        examine_clip_order,
        accept_clip_order,
        refuse_clip_order,
        unpublish_list,
        publish_list,
        own_msg_list,
        receive_msg_list,
        view_msg1,
        view_msg2,
        user_list,
        set_admin,
        set_commission,
        admin_list,
        cancel_admin,
        edit_global,
        upload_global_vars,
        spread,
        spread_error,
        edit_person_info1,
        edit_person_info,
        revoke_15day,
        ws_clip_list,
        ws_copywrite_list,
        ws_examine,
        ws_menu,
        ws_task_list,
        reset_group_topic
    ];
    let driver: MySqlDriver = MySqlDriver {
        driver: "mysql://root:root@127.0.0.1:3306/c".to_string(),
        base_url: "http://127.0.0.1:8080/".to_string()
    };
    custom(config).manage(driver).mount("/", routes).mount("/static", StaticFiles::from("web")).mount("/uploads", StaticFiles::from("uploads")).launch();
}

